#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "mpi.h"
#include "palindromo.h"

#ifndef BUFFKB
	#define TAM_BUFF 4096
#else
	#define TAM_BUFF 1024*BUFFKB
#endif


#define ARQUIVO 	"./arq/wikipedia.txt"
#define ARQ_PALI	"./resultados/wiki_pal_mpi.txt"

char buffer1[TAM_BUFF]; //buffer de leitura
char buffer2[TAM_BUFF]; //buffer de gravacao

int main(int argc, char **argv)
{
	int rank;
	int nproc;
	int pfd, pfd_resul;
	int it, lido, fimb;
	int dest, ultimo;
	int tam;
	MPI_Status Stat;
 	long t1, t2;

	int palavras, palindromos, primos;
	int plflag, prflag;
	int iniciop, fimp;
	int bufval[3], buftotal[3];

	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	if (rank == 0)
	{			
		t1 = clock();		
		
		MPI_Comm_size(MPI_COMM_WORLD, &nproc);
		nproc--;
		
		if ((pfd = open(ARQUIVO, O_RDONLY, 0 )) == -1)
		{
		    fprintf(stderr, "Nao foi possivel abrir: %s.\n", ARQUIVO );
		    exit(1);
		}
		if ((pfd_resul = open(ARQ_PALI, O_WRONLY | O_CREAT | O_TRUNC,
					S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
		{
		    fprintf(stderr, "Nao foi possivel abrir: %s.\n", ARQ_PALI );
		    exit(1);
		}



		dest = it = 0;
		do{
			lido = read(pfd, (void *) buffer1, TAM_BUFF); // carrega um bloco
			it++;
			if(lido == TAM_BUFF){
				fimb = TAM_BUFF-1;
				while(fimb > 0 && (UCASE(buffer1[fimb]) >= 'A' &&
				  	UCASE(buffer1[fimb]) <= 'Z')) fimb--;
				fimb++;
				fimb -= TAM_BUFF;
				if(fimb < 0) lseek(pfd, SEEK_CUR, fimb);
			}


			//Receber confirmação de que o processo esta pronto
			MPI_Recv(&tam, 1, MPI_INT, dest+1, 1, MPI_COMM_WORLD, &Stat);
			if(tam > 0){
				//recebe caso o processo possuir resultados para entregar
				MPI_Recv(buffer2, tam , MPI_CHAR, dest+1, 2, MPI_COMM_WORLD, &Stat);
				write(pfd_resul, buffer2, tam);
			}

			//envia o bloco ao processo 
			MPI_Send(&lido, 1, MPI_INT, dest+1, 3, MPI_COMM_WORLD);
			MPI_Send(buffer1, lido, MPI_CHAR, dest+1, 4, MPI_COMM_WORLD);

			dest++;
			dest %= nproc; //proximo processo
		} while(lido == TAM_BUFF);

		lido = 0;
		ultimo = dest;
		do {
			//"avisa" os processos que nao ha mais arquivo
			MPI_Recv(&tam, 1, MPI_INT, dest+1, 1, MPI_COMM_WORLD, &Stat);
			if(tam > 0){
				//recebe os ultimos palindromos
				MPI_Recv(buffer2, tam , MPI_CHAR, dest+1, 2, MPI_COMM_WORLD, &Stat);
				write(pfd_resul, buffer2, tam);
			}
			MPI_Send(&lido, 1, MPI_INT, dest+1, 3, MPI_COMM_WORLD);
			dest++;
			dest %= nproc;
		} while(dest!=ultimo);

		//soma os resultados
		bufval[0] = bufval[1] = bufval[2] = 0;
		MPI_Reduce(bufval, buftotal, 3, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
	    palavras = buftotal[0];
		palindromos = buftotal[1];
		primos = buftotal[2];

		t2 = clock();

		printf("\nResultados:\n\n");
		printf("\tTempo total: %lfs\n", ((double) (t2 - t1))/CLOCKS_PER_SEC);
		printf("\tAcessos ao arquivo: %d\n\tPalavras: %d\n\tPalindromos: %d\n",
			  it, palavras, palindromos);
		printf("\tPrimos: %d\n", primos);

		close(pfd);
		close(pfd_resul);
	}

	else {

		tam = palavras = palindromos = primos = 0;

		//Avisa que esta pronto
		MPI_Send(&tam, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);

		//recebe a confirmacao se ha arquivo
		MPI_Recv(&lido, 1, MPI_INT, 0, 3, MPI_COMM_WORLD, &Stat);

		//Enquanto ha texto
		while(lido > 0){
			//recebe o bloco
			MPI_Recv(buffer1, lido , MPI_CHAR, 0, 4, MPI_COMM_WORLD, &Stat);
			
			/*executa como o sequencial*/

			iniciop = 0;
			while(iniciop < lido && (UCASE(buffer1[iniciop]) < 'A' ||
					 UCASE(buffer1[iniciop]) > 'Z')) iniciop++;

			tam = 0;
			while(iniciop < lido){

				fimp = iniciop;
				while(fimp < lido && (UCASE(buffer1[fimp]) >= 'A' &&
					  UCASE(buffer1[fimp]) <= 'Z')) fimp++;

				if(fimp == TAM_BUFF) break;

				palavras++;

				plflag = check_palavra( buffer1 + iniciop, fimp - iniciop);

				if(plflag){
					palindromos++;

					prflag = teste_primo( buffer1 + iniciop, fimp - iniciop);
					if(prflag) primos++;

					buffer1[fimp] = '\0';
					tam += sprintf(buffer2 + tam, "%s %d\n", buffer1 + iniciop, prflag);
				}

				iniciop = fimp;
				while(iniciop < lido && (UCASE(buffer1[iniciop]) < 'A' ||
					  UCASE(buffer1[iniciop]) > 'Z')) iniciop++;
			}

			//avisa que terminou e esta pronto
			MPI_Send(&tam, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);
			if(tam > 0){
				//Envia os palindromos
				MPI_Send(buffer2, tam, MPI_CHAR, 0, 2, MPI_COMM_WORLD);
			}
			//recebe confirmacao se ha mais texto
			MPI_Recv(&lido, 1, MPI_INT, 0, 3, MPI_COMM_WORLD, &Stat);
		}

		//soma os resultados
		bufval[0] = palavras;
		bufval[1] = palindromos;
		bufval[2] = primos;
		MPI_Reduce(bufval, buftotal, 3, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
	}

	MPI_Finalize();
	return 0;
}

